#pragma once

#include "NodoSimple.h"
#include "NodoDoble.h"
#include "ListaCircular.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

template <class T>
class ListaDoble
{
private:
    
    NodoDoble<T>* primero;  //puntero al inicio
	NodoDoble<T>* actual;
	int cantidad;
	string flag;
public: 
	ListaDoble();
	ListaDoble(ListaDoble *);  //constructor copia
	virtual ~ListaDoble();

	string getFlag();
	void setFlag(string);
	NodoDoble<T>* getPrimero();
	NodoDoble<T>* getActual();
	void setActual(NodoDoble<T> *);
    void setPrimero(NodoDoble<T> *);
	void insertarUltimo(T*);
	void insertarPrimero(T *);
	int eliminar(int);		
	string toString();
    T * buscar(int num);
    NodoDoble<T>* buscarNodo(int num);
    void insertarPos(int pos, T*e);
    void eliminarPos(int pos);
    void intercambiar(int pos, int posb);//posicion a intercambiar
	int getCantidad();
	void setCantidad(int);
};

template<class T>
string ListaDoble<T>::getFlag(){
   return flag;
}

template<class T>
void ListaDoble<T>::setFlag(string a){
	flag = a;
}


template<class T>
NodoDoble<T>* ListaDoble<T>::getActual(){
   return actual;


}



template<class T>
NodoDoble<T>* ListaDoble<T>::getPrimero(){
   return primero;


}

template<class T>
void ListaDoble<T>::setActual(NodoDoble<T> *act){

actual=act;

}

template<class T>
void ListaDoble<T>::setCantidad(int c){

cantidad = c;

}

template<class T>
void ListaDoble<T>::setPrimero(NodoDoble<T> *prim){

primero=prim;

}


template<class T>
ListaDoble<T>::ListaDoble()
{
	actual = primero = NULL; 
	cantidad = 0;
}

template<class T>
ListaDoble<T>::ListaDoble(ListaDoble<T> *list)
{   
	
	list->setActual(list->getPrimero());
	while(list->getActual() != NULL)
	{
		insertarPrimero(new T(list->getActual()->getInfo()));
		list->setActual(list->getActual()->getSigNodo());
	}
}


template<class T>
ListaDoble<T>::~ListaDoble()
{
	while(primero!=NULL)
	{
		actual = primero;
	    primero=primero->getSigNodo();
	    delete actual;
	}

}

template<class T>
void ListaDoble<T>::insertarPrimero(T * c)
{
	if(primero == NULL)
	{
		primero = new NodoDoble<T>(NULL,c, NULL);
	}
	else
	{
		actual = new NodoDoble<T>(NULL,c, primero);
        primero->setAntNodo(actual);
		primero = actual;
	}
	cantidad++;
}

template<class T>
void ListaDoble<T>::insertarUltimo(T * c)
{
	if(primero == NULL)
	{
		primero = new NodoDoble<T>(NULL,c, NULL);
	}
	else
	{
		NodoDoble<T>* ant;
		actual = primero;
		while(actual != NULL)
		{
			ant = actual;
			actual = actual->getSigNodo();
		}
		actual = new NodoDoble<T>(ant,c, NULL);
		ant->setSigNodo(actual);
	}
	cantidad++;
}

template<class T>
int ListaDoble<T>::eliminar(int num){
	
	int ret = 0;
	if(primero!=NULL)
	{
	    actual = primero;
		if(*(primero->getInfo())== num)
		{
			primero=primero->getSigNodo();
			delete actual;
			cantidad--;
            return ret = 1;
		}
		else
		{

			NodoDoble<T> *aux;
			while(actual->getSigNodo()!=NULL)
			{   
				if(*(actual->getSigNodo()->getInfo())==num)
				{
					aux=actual->getSigNodo();
					actual->setSigNodo(actual->getSigNodo()->getSigNodo());
                    actual->getSigNodo()->getSigNodo()->setAntNodo(actual);
					delete aux;
					cantidad--;
					return ret = 1;
				}
				actual=actual->getSigNodo();
			}
		}
	}
	return ret;
}

template<class T>
string ListaDoble<T>::toString()
{
	stringstream s;
    if(primero==NULL)
    	s<<"La lista esta vacia";
    else
    {
    	actual = primero;
    	while(actual != NULL)
    	{
    		s<<"\n\n";
			s<< actual->getInfo()->toString()<<endl;
    		actual = actual->getSigNodo();
    	}
    }
    return s.str();
}

template<class T>
T* ListaDoble<T>::buscar(int num){

    actual=primero;
	while(actual!=NULL)
	{
		if(*(actual->getInfo())==num)
		{
			return (actual->getInfo());
		}

		actual=actual->getSigNodo();
	}
	return NULL;
}

template<class T>
NodoDoble<T>* ListaDoble<T>::buscarNodo(int num){

    actual=primero;
	while(actual!=NULL)
	{
		if(*(actual->getInfo())==num)
		{
			return actual;
		}

		actual=actual->getSigNodo();
	}
	return NULL;
}

template<class T>
void ListaDoble<T>::insertarPos(int pos, T* e) //comienza en 1
{

    if(pos == cantidad+1)
    {
        this->insertarUltimo(e);
    }
    else if(pos == 1)
    {
        actual = primero;
        int num=2;
        while(actual != NULL)
        {
            actual->getInfo()->setNumero(num++);
            actual = actual->getSigNodo();
        }
        this->insertarPrimero(e);
    }
    else
    {
        actual = primero;
        for(int i=1;i<pos;i++) //me da la posicion a reemplazar
        {
            actual = actual->getSigNodo();
        }
        NodoDoble<T>* aux = new NodoDoble<T>(NULL, e, actual);
        if(actual->getAntNodo()!=NULL)
        {
            aux->setAntNodo(actual->getAntNodo());
            actual->getAntNodo()->setSigNodo(aux);
        }
        actual->setAntNodo(aux);
        int num=pos+1;
        while(actual != NULL)
        {
            actual->getInfo()->setNumero(num++);
            actual = actual->getSigNodo();
        }
    }
}

template<class T>
void ListaDoble<T>::eliminarPos(int pos)
{
    if(primero != NULL)
    {
        if(pos == 1)
        {
            NodoDoble<T>* aux = primero;
            primero = primero->getSigNodo();
            delete aux;   
            cantidad --;
            actual = primero;
            int num=1;
            while(actual != NULL)
            {
                actual->getInfo()->setNumero(num++);
                actual = actual->getSigNodo();
            }
        }
        else
        {
            NodoDoble<T>* aux;
            actual = buscarNodo(pos);
            if(actual != NULL)
            {
                if(actual->getAntNodo() != NULL)
                {
                    actual->getAntNodo()->setSigNodo(actual->getSigNodo());
                }
                if(actual->getSigNodo()!=NULL)
                {
                    actual->getSigNodo()->setAntNodo(actual->getAntNodo());
                }
                aux = actual->getSigNodo();
                delete actual;
                cantidad --;
                int num=pos;
                while(aux != NULL)
                {
                    aux->getInfo()->setNumero(num++);
                    actual = actual->getSigNodo();
                }
            }
        }
    }
}


template<class T>
int ListaDoble<T>::getCantidad()
{
	return cantidad;
}

template<class T>
void ListaDoble<T>::intercambiar(int posA, int posB)//intrcambiar posA con posB
{
    if(primero != NULL && primero->getSigNodo() != NULL)
    {
        NodoDoble<T>* A = buscarNodo(posA);

        NodoDoble<T>* B = buscarNodo(posB);

        T* aux = A->getInfo();
        A->setInfo(B->getInfo());
        B->setInfo(aux);
        
        int num = A->getInfo()->getNumero();
        A->getInfo()->setNumero(B->getInfo()->getNumero());
        B->getInfo()->setNumero(num);
    }
}
